/*
 * Copyright (c) 2018, dreamkaylee@foxmail.com All Rights Reserved.
 */

package com.vip.jwt.common.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import com.vip.jwt.common.entity.FileInfo;
import com.vip.jwt.common.exception.ExceptionEnum;
import com.vip.jwt.common.exception.MyException;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * SpringMVC 文件上传
 *
 * @author vip
 * @date 2018-04-08 9:31
 */
public class FileUtil {

    /**
     * 文件夹格式
     */
    private final static String YYYYMMDD = "yyyyMMdd";

    @Getter
    private static String MODULE_NAME;

    @Value("${module-name}")
    public void setModuleName(String moduleName) {
        MODULE_NAME = moduleName;
    }

    @Getter
    private static String FILE_REAL_PATH;

    @Value("${upload-file-path}")
    public void setFileRealPath(String fileRealPath) {
        FILE_REAL_PATH = fileRealPath;
    }

    /**
     * 服务器Url转换本地Url
     * @param serverUrl 服务器地址
     * @return
     */
    public static String getLocalUrl(String serverUrl){
        return FILE_REAL_PATH + serverUrl;
    }

    /**
     * 本地Url转服务器Url
     * @param localUrl 本地地址
     * @return
     */
    public static String getServerUrl(String localUrl){
        return localUrl.replaceAll(FILE_REAL_PATH,"");
    }

    /**
     * 文件上传
     * @param file 文件
     * @return
     */
    public static FileInfo uploadFile(MultipartFile file) {
        /**
         * 检查目录权限
         */
        if (checkDirectory()) {
            /**
             * 创建文件夹
             */
            Map<String, String> path = mkdirs(FILE_REAL_PATH);
            String saveUrl = path.get("url");
            String savePath = path.get("path");
            if (file != null) {
                /**
                 * 获取上传文件名
                 */
                String fileName = file.getOriginalFilename();
                if ("".equals(Objects.requireNonNull(fileName).trim())) {
                    throw new MyException(ExceptionEnum.FILE_NOT_FOUND);
                }
                /**
                 * 获取文件后缀
                 */
                String noSuffixFileName = fileName.substring(0,fileName.lastIndexOf("."));
                String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
                String newFileName = UUID.fastUUID() + "." + suffix;
                File uploadedFile = new File(savePath, newFileName);
                try {
                    /**
                     * 将上传文件写到服务器上指定的文件夹
                     */
                    file.transferTo(uploadedFile);
                } catch (Exception e) {
                    throw new MyException(ExceptionEnum.FILE_UPLOAD_FAIL);
                }
                //本地路径
                String localUrl = uploadedFile.getPath();
                //服务器路径
                String serverUrl = saveUrl + newFileName;
                FileInfo fileInfo = new FileInfo();
                fileInfo.setFileName(noSuffixFileName);
                fileInfo.setSuffix(suffix);
                fileInfo.setNewFileName(newFileName);
                fileInfo.setLocalUrl(localUrl);
                fileInfo.setServerUrl(serverUrl);
                return fileInfo;
            }
        }
        return null;
    }

    /**
     * 文件删除
     * @param serverUrl 数据库里面存储的路径
     */
    public static void deleteFile(String serverUrl){
        if(checkDirectory()){
            String localUrl = FILE_REAL_PATH+serverUrl;
            File file = new File(localUrl);
            if(file.isFile()){
                file.delete();
            }
        }
    }

    /**
     * 获取所有的文件名
     * @return
     */
    public static List<String> getFileNames(){
        List<String> fileNames = new ArrayList<>();
        getFiles(FILE_REAL_PATH, fileNames);
        return fileNames;
    }

    /**
     * 根据日期获取所有的文件名
     * @param date
     * @return
     */
    public static List<String> getFileNameByDate(Date date){
        String dateStr = DateUtil.format(date,YYYYMMDD);
        List<String> newFileNames = new ArrayList<>();
        List<String> fileNames = new ArrayList<>();
        getFiles(FILE_REAL_PATH, fileNames);
        for (String fileName : fileNames) {
            if (fileName.contains(dateStr)) {
                newFileNames.add(fileName);
            }
        }
        return newFileNames;
    }

    /**
     * 根据日期获取所有的文件名(日期字符串格式：yyyyMMdd)
     * @param dateStr
     * @return
     */
    public static List<String> getFileNameByDate(String dateStr){
        List<String> newFileNames = new ArrayList<>();
        List<String> fileNames = new ArrayList<>();
        getFiles(FILE_REAL_PATH, fileNames);
        for (String fileName : fileNames) {
            if (fileName.contains(dateStr)) {
                newFileNames.add(fileName);
            }
        }
        return newFileNames;
    }

    /**
     * 递归获取文件
     * @param path 文件夹路径
     * @param fileNames 存放文件路径的集合
     * @return
     */
    private static void getFiles(String path,List<String> fileNames){
        File file = new File(path);
        //如果这个路径是文件夹
        if(file.isDirectory()){
            File[] files = file.listFiles();
            assert files != null;
            for (File value : files) {
                //如果是文件夹
                if (value.isDirectory()) {
                    //递归
                    getFiles(value.getPath(), fileNames);
                } else {
                    //获取文件
                    String filePath = value.getPath();
                    String onlineUrl = filePath.substring(filePath.indexOf("\\"), filePath.length());
                    onlineUrl = onlineUrl.replace('\\', '/');
                    fileNames.add(onlineUrl);
                }
            }
        }
    }

    /**
     * 检查上传目录权限
     *
     * @return
     */
    private static boolean checkDirectory() {
        File uploadDir = new File(FILE_REAL_PATH);
        if (!uploadDir.isDirectory()) {
            throw new RuntimeException("目录不存在");
        }
        // 检查目录写权限
        if (!uploadDir.canWrite()) {
            throw new RuntimeException("目录没有写权限");
        }
        return true;
    }

    /**
     * 创建文件夹
     *
     * @param path
     * @return
     */
    private static Map<String, String> mkdirs(String path) {
        String savePath = path + "/";
        String saveUrl = "/";
        File saveDirFile = new File(savePath);
        if (!saveDirFile.exists()) {
            saveDirFile.mkdirs();
        }
        SimpleDateFormat sdf = new SimpleDateFormat(YYYYMMDD);
        String ymd = sdf.format(DateUtil.date());
        savePath += MODULE_NAME + "/" + ymd + "/";
        saveUrl += MODULE_NAME + "/"  + ymd + "/";
        File dirFile = new File(savePath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }
        Map<String, String> map = new HashMap<>(16);
        map.put("url", saveUrl);
        map.put("path", savePath);
        return map;
    }

    /**
     * File转字节数组
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] fileToBytes(File file) throws IOException{
        InputStream is = new FileInputStream(file);
        long length = file.length();
        byte[] bytes = new byte[(int) length];
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
                && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }
        if (offset < bytes.length) {
            is.close();
            throw new IOException("【"+file.getName()+"】文件转换字节数据失败");
        }
        is.close();
        return bytes;
    }

    /**
     * 字节数组转base64字符串
     * @param bytes
     * @return
     */
    public static String bytesToBase64(byte[] bytes){
        //将字节转换成Base64字符串
        return Base64.encode(bytes);
    }

    /**
     * File转Base64字符串
     * @param file
     * @return
     */
    public static String fileToBase64(File file){
        return Base64.encode(file);
    }

    /**
     * 图片地址转Base64字符串
     * @param serverUrl
     * @return
     */
    public static String serverUrlToBase64(String serverUrl) throws FileNotFoundException {
        String suffix = serverUrl.substring(serverUrl.lastIndexOf(".") + 1);
        String localUrl = FILE_REAL_PATH + serverUrl;
        // 前缀
        String prefix = "data:image/" + suffix + ";base64,";
        InputStream in = new FileInputStream(localUrl);
        String encodeStr = Base64.encode(in);
        return prefix + encodeStr;
    }

    /**
     * MultipartFile类型图片转换base64字符串
     * @param multipartFile
     * @return
     */
    public static String multipartFileToBase64(MultipartFile multipartFile) throws IOException {
        String fileName = multipartFile.getOriginalFilename();
        assert fileName != null;
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        // 前缀
        String prefix = "data:image/" + suffix + ";base64,";
        InputStream in = multipartFile.getInputStream();
        String encodeStr = Base64.encode(in);
        return prefix + encodeStr;
    }

    /**
     * BufferedImage类型图片转换base64字符串
     * @param bufferedImage
     * @return
     */
    public static String bufferedImageToBase64(BufferedImage bufferedImage,String suffix) throws IOException {
        // 创建io流
        ByteArrayOutputStream baas = new ByteArrayOutputStream();
        // 写入流中
        ImageIO.write(bufferedImage, "png", baas);
        // 转换成字节
        byte[] bytes = baas.toByteArray();
        // 转换成base64串
        String base64Str = Base64.encode(bytes).trim();
        // 删除 \r\n
        base64Str = base64Str.replaceAll("\n", "").replaceAll("\r", "");
        // 前缀
        String prefix = "data:image/" + suffix + ";base64,";
        return prefix + base64Str;
    }

    /**
     * 下载
     *
     * @param fileName
     * @param filePath
     * @return
     * @throws IOException
     */
    @SuppressWarnings("resource")
    public static ResponseEntity<byte[]> downloadFile(String fileName, String filePath) throws IOException {
        File file = new File(FILE_REAL_PATH + filePath);
        byte[] body = null;
        InputStream is = new FileInputStream(file);
        body = new byte[is.available()];
        is.read(body);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition", "attchement;filename=" + fileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        HttpStatus statusCode = HttpStatus.OK;
        return new ResponseEntity<byte[]>(body, headers, statusCode);
    }

}
